﻿#region

using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using CuongHanh.DAL;
using CuongHanh.Models;

#endregion

namespace CuongHanh.Context
{
    public class MultiCache<T>
            where T : BaseClassMultiCache, new()
    {
        public static MultiCache<T> Singleton = new MultiCache<T>();

        private readonly Dictionary<int, Dictionary<int, T>> _cacheList = new Dictionary<int, Dictionary<int, T>>();

        private readonly Dictionary<int, T> _cacheObject = new Dictionary<int, T>();

        private readonly object _lockCache = new object();

        private Func<T, T, T> _func;

        public MultiCache()
        {
            LoadCache();
        }

        public void SetFuncUpdateModel(Func<T, T, T> func)
        {
            _func = func;
        }

        private void LoadCache()
        {
            lock (_lockCache)
            {
                using (UnitOfWork unitOfWork = new UnitOfWork())
                {
                    IQueryable<T> models = unitOfWork.Where<T>(c=>!c.Inactive);
                    foreach (T model in models)
                    {
                        if(model.KeyList.HasValue
                           && !_cacheList.ContainsKey(model.KeyList.Value))
                            _cacheList[model.KeyList.Value] = new Dictionary<int, T>();
                        if(model.KeyList.HasValue)
                            _cacheList[model.KeyList.Value][model.KeyObject] = model;
                        _cacheObject[model.KeyObject] = model;
                    }
                }
            }
        }

        public IEnumerable<T> GetCacheByKeyList(int keyList)
        {
            lock (_lockCache)
            {
                return _cacheList.ContainsKey(keyList)
                        ? _cacheList[keyList].Values.ToList()
                        : new List<T>();
            }
        }

        public T GetCacheByKeyObject(int keyObject)
        {
            lock (_lockCache)
            {
                return _cacheObject.ContainsKey(keyObject)
                        ? _cacheObject[keyObject]
                        : null;
            }
        }

        //public bool DeleteCache(T model)
        //{
        //    lock (_lockCache)
        //    {
        //        return DeleteCacheByKeyObject(model)
        //               && DeleteCacheByKeyList(model);
        //    }
        //}

        private bool DeleteCacheByKeyList(T model)
        {
            if(!model.KeyList.HasValue
               || !_cacheList.ContainsKey(model.KeyList.Value)
               || !_cacheList[model.KeyList.Value].ContainsKey(model.KeyObject))
                return false;
            _cacheList[model.KeyList.Value].Remove(model.KeyObject);
            return true;
        }

        private bool DeleteCacheByKeyObject(T model)
        {
            if(!_cacheObject.ContainsKey(model.KeyObject))
                return false;
            _cacheObject.Remove(model.KeyObject);
            return true;
        }

        public bool UpdateCached(T model)
        {
            lock (_lockCache)
            {
                //update cache
                if(!model.Inactive)
                {

                    if(!_cacheObject.ContainsKey(model.KeyObject))
                    {
                        _cacheObject[model.KeyObject] = model;
                    }
                    T data = _cacheObject[model.KeyObject];
                    UpdateModel(_func,
                            data,
                            model);

                    #region update for cache list

                    if(data.KeyList.HasValue
                       && !_cacheList.ContainsKey(data.KeyList.Value))
                        _cacheList[data.KeyList.Value] = new Dictionary<int, T>();
                    if(data.KeyList.HasValue)
                        _cacheList[data.KeyList.Value][data.KeyObject] = data;

                    #endregion
                }
                else
                {
                    //delete cache
                    return DeleteCacheByKeyObject(model)
                           && DeleteCacheByKeyList(model);
                }
                return true;
            }
        }

        private T UpdateModel(Func<T, T, T> func,
                T oldModel,
                T newModel)
        {
            return func(oldModel,
                    newModel);
        }

        public IEnumerable<T> GetObjectsByFilter(Expression<Func<T, bool>> filter = null)
        {
            lock (_lockCache)
            {
                if (filter != null)
                {
                    return _cacheObject.Values.AsQueryable().Where(filter);
                }
                return _cacheObject.Values;
            }
        }
    }
}
